package com.ease.util.logging {

   import com.ease.util.MxmlDeserializer;
   import com.ease.util.ReflectionUtil;

   import mx.core.Application;
   import mx.core.FlexGlobals;
   import mx.logging.ILogger;
   import mx.logging.ILoggingTarget;
   import mx.logging.LogEventLevel;


   /**
    * Represents the logging xml configuration.
    * The typical usage is to initialize the instance with the
    * configuration xml, then use the <code>targets</code> property to
    * retrieve the <code>ILoggingTarget</code> objects.
    */
   internal class LogConfiguration {
      [ArrayElementType("mx.logging.ILoggingTarget")]
      private var _targets:Array = [];

      private var _defaultTarget:ILoggingTarget;

      private static var _logger:ILogger =
         LogManager.getLogger(LogConfiguration);

      /**
       * The request parameter name indicating the log level.
       */
      private static const LOG_LEVEL_PARAM:String = "logLevel";

      public function LogConfiguration(configXml:XML) {
         var defaultTargetId:String = configXml.@defaultTarget;
         var targetXmls:XMLList = configXml.target;

         // deserialize the default target first because it is used to
         // set the default values for the other targets properties.
         var defaultTargetXml:XML;
         if (defaultTargetId.length != 0) {
            var xmll:XMLList = targetXmls.
               (hasOwnProperty("@id") && @id == defaultTargetId);
            if (xmll.length() == 1) {
               defaultTargetXml = xmll[0];
               _defaultTarget = createTarget(defaultTargetXml);
            } else {
               _logger.error("invalid defaultTargetId: " + defaultTargetId);
            }
         }

         for each(var targetXml:XML in targetXmls) {
            var target:ILoggingTarget;
            // We already created the default target.
            if (targetXml == defaultTargetXml) {
               target = _defaultTarget;
            } else {
               target = createTarget(targetXml);
            }
            if (target != null) {
               _targets.push(target);
            }
         }
      }

      [ArrayElementType("mx.logging.ILoggingTarget")]
      /**
       * Returns the <code>ILoggingTarget</code> objects in this
       * configuration.
       */
      public function get targets():Array {
         return _targets;
      }

      /**
       * Returns the <code>ILoggingTarget</code> for the given
       * <code>target</code> tag.
       */
      private function createTarget(targetXml:XML):ILoggingTarget {
         var isDebugTarget:Boolean = !(targetXml.@debug == "false");
         var isDebugBuild:Boolean;
//         config::DEBUG {
            isDebugBuild = true;
//         }
         // ignore debug-only targets in non-debug builds.
         if (isDebugTarget && !isDebugBuild) {
            return null;
         }

         var type:String = targetXml.@type;

         // Remove the attributes used internally by the framework from the object
         // tag before calling the serializer so that it doesn't get interpreted
         // as a property of the object.
         delete targetXml.@id;
         delete targetXml.@type;
         delete targetXml.@debug;

         var deserializer:MxmlDeserializer = new MxmlDeserializer(null/*loader*/,
            false, initializeTarget, resolveTargetProperty);
         var result:ILoggingTarget = deserializer.xmlToObject(targetXml,
            type) as ILoggingTarget;
         return result;
      }

      /**
       * An MxmlSerializer object initializer used to initialize
       * the logging target properties using the default target if any
       * is specified.
       */
      private function initializeTarget(object:Object):void {
         if (_defaultTarget == null) {
            return;
         }
         var target:ILoggingTarget = object as ILoggingTarget;
         if (target == null) {
            return;
         }
         var defaultPropNames:Array /*String*/
         = ReflectionUtil.getPropertyNames(_defaultTarget);
         for each (var propName:String in defaultPropNames) {
            if (!object.hasOwnProperty(propName)) {
               continue;
            }
            try {
               object[propName] = _defaultTarget[propName];
            } catch (error:Error) {
               // ignore if the destination target has the property
               // readonly (like id) or with a different type.
            }
         }
      }

      /**
       * An MxmlSerializer property resolver used to support
       * specifying the level as the LogEventLevel constant strings
       * instead of their (rude) numeric values.
       */
      private function resolveTargetProperty(object:Object,
                                             propertyName:String,
                                             propertyValue:XML):Boolean {
         // convert the level string specified in the xml to its
         // equivalent level int value.
         if (!(object is ILoggingTarget)
            || propertyName != "level") {
            return false;
         }

         var levelStr:String;

         // check if full logging is specified in the url.
         var params:Object = Application(FlexGlobals.topLevelApplication).parameters;
         if (params != null && params.hasOwnProperty(LOG_LEVEL_PARAM) ) {
            levelStr = params[LOG_LEVEL_PARAM];
         } else {
            levelStr = propertyValue.toString();
         }
         levelStr = levelStr.toUpperCase();

         var level:int;
         switch (levelStr) {
            case "DEBUG":
               level = LogEventLevel.DEBUG;
               break;
            case "ERROR":
               level = LogEventLevel.ERROR;
               break;
            case "FATAL":
               level = LogEventLevel.FATAL;
               break;
            case "INFO":
               level = LogEventLevel.INFO;
               break;
            case "WARN":
               level = LogEventLevel.WARN;
               break;
            default:
               if (levelStr != "ALL") {
                  _logger.error("Invalid logging level: " + levelStr
                     + ", allowed values are FATAL, ERROR, WARN, INFO, DEBUG, ALL.");
               }
               level = LogEventLevel.ALL;
               break;
         }
         ILoggingTarget(object).level = level;
         return true;
      }
   }

}
